﻿

using System.Net;
using System.Net.Sockets;

namespace S7Communication
{
    public enum PLC_TYPE
    {
        S7_1200 = 0x01,
        S7_1500 = 0x02,
    }
    /// <summary>
    /// 控制状态
    /// </summary>
    public enum CONTROL_STATE
    {
        /// <summary>
        /// 就绪
        /// </summary>
        Ready = 0,
        /// <summary>
        /// 控制中
        /// </summary>
        Busy = 1,
        /// <summary>
        /// 完成
        /// </summary>
        Done = 2,
        /// <summary>
        /// 中止
        /// </summary>
        CommandAboted = 4,
        /// <summary>
        /// 错误
        /// </summary>
        Error = 5,
        /// <summary>
        /// 超时
        /// </summary>
        TimeOut = 6,
    }
    /// <summary>
    /// 表示层PDU类型
    /// </summary>
    public enum COTP_PDU_TYPE
    {
        /// <summary>
        /// ED Expedited Data，加急数据
        /// </summary>
        ED = 0x01,
        /// <summary>
        /// EA Expedited Data Acknowledgement，加急数据确认
        /// </summary>
        EA = 0x02,
        /// <summary>
        ///  UD，用户数据
        /// </summary>
        UD = 0x04,
        /// <summary>
        /// RJ Reject，拒绝
        /// </summary>
        RJ = 0x05,
        /// <summary>
        /// AK Data Acknowledgement，数据确认
        /// </summary>
        AK = 0x06,
        /// <summary>
        /// ER TPDU Error，TPDU错误
        /// </summary>
        ER = 0x07,
        /// <summary>
        /// DR Disconnect Request，断开请求
        /// </summary>
        DR = 0x08,
        /// <summary>
        /// DC Disconnect Confirm，断开确认
        /// </summary>
        DC = 0x0c,
        /// <summary>
        /// Connect Confirm，连接确认
        /// </summary>
        CC = 0x0d,
        /// <summary>
        /// CR Connect Request，连接请求
        /// </summary>
        CR = 0x0e,
        /// <summary>
        /// DT Data，数据传输
        /// </summary>
        DT = 0x0f
    }
    /// <summary>
    /// 数据传输作业类型
    /// </summary>
    public enum DT_ROSCTR
    {
        /// <summary>
        /// 作业请求，如，读/写存储器，读/写块，启动/停止设备，设置通信
        /// </summary>
        JOB = 0x01,
        /// <summary>
        ///确认响应，这是一个没有数据的简单确认
        /// </summary>
        ACK = 0x02,
        /// <summary>
        /// 确认数据响应，一般是响应JOB的请求
        /// </summary>
        ACK_DATA = 0x03,
        /// <summary>
        /// 原始协议的扩展，参数字段包含请求/响应ID（用于编程/调试，读取SZL，安全功能，时间设置，循环读取...）
        /// </summary>
        USERDATA = 0x07
    }
    /// <summary>
    /// 数据传输参数功能码
    /// </summary>
    public enum DT_PARAMETER_FUCTION
    {
        /// <summary>
        /// CPU服务
        /// </summary>
        CPU_Services = 0x00,
        /// <summary>
        /// 建立通信
        /// </summary>
        Setup_Communication = 0xf0,
        /// <summary>
        ///  读取值
        /// </summary>
        Read_Variable = 0x04,
        /// <summary>
        /// 写入值
        /// </summary>
        Write_Variable = 0x05,
        /// <summary>
        /// 请求下载
        /// </summary>
        Request_Download = 0x1a,
        /// <summary>
        /// 下载块
        /// </summary>
        Download_Block = 0x1b,
        /// <summary>
        /// 下载结束
        /// </summary>
        Download_Ended = 0x1c,
        /// <summary>
        /// 开始上传
        /// </summary>
        Start_Upload = 0x1d,
        /// <summary>
        /// 上传
        /// </summary>
        Upload = 0x1e,
        /// <summary>
        /// 上传结束
        /// </summary>
        End_Upload = 0x1f,
        /// <summary>
        /// 程序调用服务
        /// </summary>
        PI_Service = 0x28,
        /// <summary>
        /// 关闭PLC
        /// </summary>
        PLC_Stop = 0x29
    }
    public enum DT_DATA_SYNTAX_ID
    {
        /// <summary>
        /// S7绝对寻址-DB1.DBX10.2
        /// </summary>
        S7ANY = 0x10,
        /// <summary>
        /// R_ID for PBC
        /// </summary>
        PBC_R_ID = 0x13,
        /// <summary>
        /// Alarm lock/free dataset
        /// </summary>
        ALARM_LOCKFREE = 0x15,
        /// <summary>
        /// Alarm indication dataset
        /// </summary>
        ALARM_IND = 0x16,
        /// <summary>
        /// Alarm acknowledge message dataset
        /// </summary>
        ALARM_ACK = 0x19,
        /// <summary>
        /// Alarm query request dataset
        /// </summary>
        ALARM_QUERYREQ = 0x1a,
        /// <summary>
        /// Notify indication dataset
        /// </summary>
        NOTIFY_IND = 0x1c,
        /// <summary>
        /// seen on Drive ES Starter with routing over S7
        /// </summary>
        DRIVEESANY = 0xa2,
        /// <summary>
        /// S7符号寻址
        /// </summary>
        S7SYM = 0xb2,
        /// <summary>
        /// Kind of DB block read, seen only at an S7-400
        /// </summary>
        DBREAD = 0xb0,
        /// <summary>
        /// Sinumerik NCK HMI access
        /// </summary>
        NCK = 0x82
    }
    public enum DT_DATA_TRANSPORT_SIZI
    {
        /// <summary>
        /// 未定义
        /// </summary>
        Null = 0x00,
        /// <summary>
        /// 按位计算长度
        /// </summary>
        BIT = 0x01,
        /// <summary>
        /// 按字节计算长度
        /// </summary>
        BYTE = 0x02,
        /// <summary>
        /// bit access, 按位计算长度
        /// </summary>
        Bit = 0x03,
        /// <summary>
        /// byte/word/dword access, 按位计算长度
        /// </summary>
        Byte_Word_DWord = 0x04,
        /// <summary>
        /// integer access, 按位计算长度
        /// </summary>
        Int = 0x05,
        /// <summary>
        /// integer access, 按字节计算长度
        /// </summary>
        DInt = 0x06,
        /// <summary>
        /// real access, 按字节计算长度
        /// </summary>
        Real = 0x07,
        /// <summary>
        /// octet string, 按字节计算长度
        /// </summary>
        Octet_String = 0x09
    }
    public enum DT_DATA_AREA
    {
        /// <summary>
        /// 200系列系统信息
        /// </summary>
        SystemInfoOf200Family = 0x03,
        /// <summary>
        /// 200系列系统标志
        /// </summary>
        SystemFlagsOf200Family = 0x05,
        /// <summary>
        /// 200系列模拟量输入
        /// </summary>
        AnalogInputsOf200Family = 0x06,
        /// <summary>
        /// 200系列模拟量输出
        /// </summary>
        AnalogOutputsOf200Family = 0x07,
        /// <summary>
        /// 直接访问外设
        /// </summary>
        DirectPeripheralAccess_P = 0x80,
        /// <summary>
        /// 输入（I）
        /// </summary>
        Inputs = 0x81,
        /// <summary>
        /// 输出（Q）
        /// </summary>
        Outputs = 0x82,
        /// <summary>
        /// 内部标志（M）
        /// </summary>
        Flags = 0x83,
        /// <summary>
        /// 数据块（DB）
        /// </summary>
        DataBlocks = 0x84,
        /// <summary>
        /// 背景数据块（DI）
        /// </summary>
        InstanceDataBlocks = 0x85,
        /// <summary>
        /// 局部变量（L）
        /// </summary>
        LocalData = 0x86,
        /// <summary>
        /// 全局变量（V）
        /// </summary>
        UnknownYet = 0x87,
        /// <summary>
        /// S7计数器（C）
        /// </summary>
        S7Counters = 0x1c,
        /// <summary>
        /// S7定时器（T）
        /// </summary>
        S7Timers = 0x0d,
        /// <summary>
        /// IEC计数器（200系列）
        /// </summary>
        IEC_Counters200Family = 0x1e,
        /// <summary>
        /// IEC定时器（200系列）
        /// </summary>
        IEC_Timers200Family = 0x1f
    }
    public enum DT_RETRUN_CODE
    {
        /// <summary>
        /// 未定义，预留
        /// </summary>
        Reserved = 0x00,
        /// <summary>
        /// 硬件错误
        /// </summary>
        HardwareError = 0x01,
        /// <summary>
        /// 对象不允许访问
        /// </summary>
        AccessingNotAllowed = 0x03,
        /// <summary>
        /// 无效地址，所需的地址超出此PLC的极限
        /// </summary>
        InvalidAddress = 0x05,
        /// <summary>
        /// 数据类型不支持
        /// </summary>
        DataTypeNotSupported = 0x06,
        /// <summary>
        /// 日期类型不一致
        /// </summary>
        DateTypeInconsistent = 0x07,
        /// <summary>
        /// 对象不存在
        /// </summary>
        ObjectNnotExist = 0x0a,
        /// <summary>
        /// 成功
        /// </summary>
        Success = 0xff
    }
    public enum VARIABLE_TYPE
    {
        /// <summary>
        /// 1位布尔型
        /// </summary>
        Bool = 0x00,
        /// <summary>
        /// 8位字节
        /// </summary>
        Byte = 0x01,
        /// <summary>
        /// 16位字
        /// </summary>
        Word = 0x02,
        /// <summary>
        /// 16位整型
        /// </summary>
        Int = 0x03,
        /// <summary>
        /// 16位无符号整型
        /// </summary>
        UInt = 0x04,
        /// <summary>
        /// 32位双字
        /// </summary>
        DWord = 0x05,
        /// <summary>
        /// 32位整型
        /// </summary>
        DInt = 0x06,
        /// <summary>
        /// 32位无符号整型
        /// </summary>
        UDInt = 0x07,
        /// <summary>
        /// 32位浮点型
        /// </summary>
        Real = 0x08,
        /// <summary>
        /// 64位长整型
        /// </summary>
        LInt = 0x09,
        /// <summary>
        /// 64位无符号长整型
        /// </summary>
        ULInt = 0x0a,
        /// <summary>
        /// 64位浮点型
        /// </summary>
        LReal = 0x0b,
        /// <summary>
        /// 字符串
        /// </summary>
        String = 0x0c,
    }
    public class AnalysisData
    {
        /// <summary>
        /// 版本号
        /// </summary>
        public byte tpkt_version;
        /// <summary>
        /// 预留
        /// </summary>
        public byte tpkt_reserve;
        /// <summary>
        /// 数据总长度
        /// </summary>
        public ushort tpkt_length;
        /// <summary>
        /// 表示层数据长度
        /// </summary>
        public byte cotp_length;
        /// <summary>
        /// 表示层pdu类型
        /// </summary>
        public COTP_PDU_TYPE cotp_pduType;
        /// <summary>
        /// 目标引用，唯一标识
        /// </summary>
        public ushort cotp_destinationRef;
        /// <summary>
        /// 源引用，唯一标识
        /// </summary>
        public ushort cotp_sourcenRef;
        /// <summary>
        /// 扩展格式/流控制
        /// </summary>
        public byte cotp_opt;
        /// <summary>
        /// 机架号
        /// </summary>
        public ushort cotp_rack;
        /// <summary>
        /// 槽号
        /// </summary>
        public ushort cotp_solt;
        /// <summary>
        /// 参数组数量
        /// </summary>
        public ushort cotp_paramsCount;
        /// <summary>
        ///参数组
        /// </summary>
        public List<COTP_Parameter> cotp_parameters;

        /// <summary>
        /// 协议ID，通常为0x32
        /// </summary>
        public byte dt_header_protocolID;
        /// <summary>
        ///   
        /// </summary>
        public DT_ROSCTR dt_header_roscrt;
        /// <summary>
        /// 预留
        /// </summary>
        public ushort dt_header_reserve;
        /// <summary>
        /// 应答序列
        /// </summary>
        public ushort dt_header_sequenceID;
        /// <summary>
        /// 参数长度
        /// </summary>
        public ushort dt_header_paramLength;
        /// <summary>
        /// 数据长度
        /// </summary>
        public ushort dt_header_dataLength;
        /// <summary>
        /// 错误等级
        /// 0x00:没有错误
        /// 0x81:应用关系
        /// 0x82:对象定义
        /// 0x83:没有可用资源
        /// 0x84:服务处理中错误
        /// 0x85:请求错误
        /// 0x87:访问错误
        /// </summary>
        public byte dt_header_errorClass;
        /// <summary>
        /// 错误代码,未定义
        /// </summary>
        public byte dt_header_errorCode;
        /// <summary>
        /// 数据传输功能码
        /// 0x00：CPU服务
        /// 0xf0：建立通信
        /// 0x04：读取值
        /// 0x05：写入值
        /// 0x1a：请求下载
        /// 0x1b：下载块
        /// 0x1c：下载结束
        /// 0x1d：开始上传
        /// 0x1e：上传
        /// 0x1f：上传结束
        /// 0x28：程序调用服务
        /// 0x29：关闭PLC
        /// </summary>
        public DT_PARAMETER_FUCTION dt_parameter_function;
        /// <summary>
        /// item数量
        /// </summary>
        public byte dt_parameter_itemCount;
        /// <summary>
        /// 调用的最大并行作业数
        /// </summary>
        public ushort dt_stepCommunication_maxAmQCalling;
        /// <summary>
        /// 已调用的最大并行作业数
        /// </summary>
        public ushort dt_stepCommunication_maxAmQCalled;
        /// <summary>
        /// 协商pdu长度
        /// </summary>
        public ushort dt_stepCommunication_pduLength;
        /// <summary>
        /// item内容
        /// </summary>
        public List<AckItem> dt_items;

        public AnalysisData()
        {
            cotp_parameters=new List<COTP_Parameter> ();
            dt_items=new List<AckItem> ();
        }
    }
    public struct COTP_Parameter
    {
        /// <summary>
        /// 参数代码
        /// 0xc0：TPDU-Size
        /// 0xc1：源节点
        /// 0xc2：目标节点
        /// </summary>
        public byte paramCode;
        /// <summary>
        /// 参数长度
        /// </summary>
        public byte paramLength;
        /// <summary>
        /// 参数内容
        /// </summary>
        public byte[] param;
    }
    public struct Variable
    {
        /// <summary>
        /// 变量名称
        /// </summary>
        public string name;
        /// <summary>
        /// 数据类型
        /// </summary>
        public VARIABLE_TYPE dateType;
        /// <summary>
        /// 数量=1为单个变量，大于1为数组
        /// </summary>
        public int Count;
        /// <summary>
        /// 确定项目结构的主要类型，通常为0x12，代表变量规范
        /// </summary>
        public byte specificationType;
        /// <summary>
        /// 本Item其余部分的长度
        /// </summary>
        public byte itemLength;
        /// <summary>
        /// 即IDS 的地址规范的格式类型，用于确定寻址模式和其余项目结构的格式     
        /// 0x10:指针寻址
        /// 0xb2:符号寻址
        /// </summary>
        public DT_DATA_SYNTAX_ID syntaxId;
        /// <summary>
        /// 数据传输大小
        /// 0：Null
        /// 1：按位为单位
        /// 2：按字为单位
        /// 3：Bit通道，按bit为单位计算长度
        /// 4：BYTE/WORD/DWORD通道，按bit为单位计算长度
        /// 5：INT通道，按bit为单位计算长度
        /// 6：DINT,按byte为单位计算长度
        /// 7：REAL，按byte为单位计算长度
        /// 9：TRING，按byte为单位计算长度
        /// </summary>
        public DT_DATA_TRANSPORT_SIZI transportSizes;
        /// <summary>
        /// 请求数据的长度
        /// </summary>
        public ushort dataLength;
        /// <summary>
        /// DB模块的编号，如果访问的不是DB区域，此处为0x0000
        /// </summary>
        public ushort dbNumber;
        /// <summary>
        /// 区域类型
        /// 0x81:输入（I）
        /// 0x82:输出（Q）
        /// 0x83:内部标志（M）
        /// 0x84:数据块（DB）
        /// </summary>
        public DT_DATA_AREA area;
        /// <summary>
        /// 地址偏移量，根据syntaxID确认偏移值，3个字节长度
        /// </summary>
        public int adrress;
        /// <summary>
        /// 返回结果
        /// </summary>
        public DT_RETRUN_CODE returnCode;
        /// <summary>
        /// 写入数据
        /// </summary>
        public byte[] data;
    }
    public struct AckItem
    {
        /// <summary>
        /// 返回码
        /// </summary>
        public DT_RETRUN_CODE returnCode;
        /// <summary>
        /// 数据传输大小
        /// 0：Null
        /// 1：按位为单位
        /// 2：按字为单位
        /// 3：Bit通道，按bit为单位计算长度
        /// 4：BYTE/WORD/DWORD通道，按bit为单位计算长度
        /// 5：INT通道，按bit为单位计算长度
        /// 6：DINT,按byte为单位计算长度
        /// 7：REAL，按byte为单位计算长度
        /// 9：TRING，按byte为单位计算长度
        /// </summary>
        public DT_DATA_TRANSPORT_SIZI transportSizes;
        /// <summary>
        /// 返回数据的长度
        /// </summary>
        public ushort dataLength;
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] data;
    }

    public class RecieveBufferAnalysis
    {
        private AnalysisData analysisData = new AnalysisData();
        public AnalysisData AnalysisData { get { return analysisData; } }
        private object Locker = new object();
        /// <summary>
        /// 接受数据解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void BufferAnalysisAsync(byte[] data)
        {
            lock(Locker)
            { 
                //会话层4个字节
                if (data == null) { throw new Exception("会话层/接收数据为空"); }
                if (data.Length < 8) { throw new Exception("会话层/接受数据太短"); }
                analysisData.tpkt_version = data[0];
                try { analysisData.tpkt_length = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray()); }
                catch (Exception ex) { throw new Exception("会话层/接受数据总长度获取失败", ex); }
                if (data.Length != analysisData.tpkt_length) { throw new Exception("会话层/接受数据总长度错误"); }
                COTP_Analysis(data.Skip(4).ToArray());
            }
        }
        /// <summary>
        /// 表示层PDU解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void COTP_Analysis(byte[] data)
        {
            analysisData.cotp_length = data[0];
            try { analysisData.cotp_pduType = (COTP_PDU_TYPE)data[1]; } catch { throw new Exception("会话层/表示层/pduType类型错误"); }
            analysisData.cotp_paramsCount = 0;
            analysisData.cotp_destinationRef = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray());
            if (data.Length - 1 != analysisData.cotp_length) { throw new Exception("会话层/表示层/数据长度错误"); }
            switch (analysisData.cotp_pduType)//pdu类型
            {
                case COTP_PDU_TYPE.ED:
                    throw new Exception("会话层/表示层/pdu类型：加急数据/暂不支持该类型");
                case COTP_PDU_TYPE.EA:
                    throw new Exception("会话层/表示层/pdu类型：加急数据确认/暂不支持该类型");
                case COTP_PDU_TYPE.UD:
                    throw new Exception("会话层/表示层/pdu类型：用户数据/暂不支持该类型");
                case COTP_PDU_TYPE.RJ:
                    throw new Exception("会话层/表示层/pdu类型：拒绝/暂不支持该类型");
                case COTP_PDU_TYPE.AK:
                    throw new Exception("会话层/表示层/pdu类型：数据确认/暂不支持该类型");
                case COTP_PDU_TYPE.ER:
                    throw new Exception("会话层/表示层/pdu类型/：TPDU错误/暂不支持该类型");
                case COTP_PDU_TYPE.DR:
                    throw new Exception("会话层/表示层/pdu类型：断开请求/暂不支持该类型");
                case COTP_PDU_TYPE.DC:
                    throw new Exception("会话层/表示层/pdu类型：断开确认/暂不支持该类型");
                case COTP_PDU_TYPE.CC:
                    if (data.Length < 7) { throw new Exception("会话层/表示层/pdu类型：连接确认/数据长度错误"); }
                    analysisData.cotp_sourcenRef = BitConverter.ToUInt16(data.Skip(4).Take(2).Reverse().ToArray());//源索引
                    analysisData.cotp_opt = data[6];
                    analysisData.cotp_parameters.Clear();
                    analysisData.cotp_paramsCount = 0;
                    if (data.Length == 7) { return; }
                    COTP_ParamenterAnalysis(data.Skip(7).ToArray());//解析参数
                    break;
                case COTP_PDU_TYPE.CR:
                    throw new Exception("会话层/表示层/pdu类型：连接请求/暂不支持该类型");
                case COTP_PDU_TYPE.DT:
                    if (data.Length < 6) { throw new Exception("会话层/表示层/pdu类型：数据传输/数据长度太短"); }
                    DT_HeaderAnalysis(data.Skip(4).ToArray());
                    break;
                default:
                    throw new Exception("会话层/表示层/pdu类型：未知");
            }
        }
        /// <summary>
        /// 表示层连接参数解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns>解析结果</returns>
        void COTP_ParamenterAnalysis(byte[] data)
        {
            int index = 0;
            while (true)
            {
                if (data.Length == index) { return; };
                if (data.Length < index + 2) { throw new Exception("会话层/表示层/pdu类型：连接确认/参数长度太短"); };
                COTP_Parameter cotp_parameter = new COTP_Parameter();
                cotp_parameter.paramCode = data[index];
                cotp_parameter.paramLength = data[index + 1];
                if (data.Length < index + 2 + cotp_parameter.paramLength) { throw new Exception("会话层/表示层/pdu类型：连接确认/参数长度错误"); }
                cotp_parameter.param = new byte[cotp_parameter.paramLength];
                Array.Copy(data, 2, cotp_parameter.param, 0, cotp_parameter.paramLength);
                analysisData.cotp_parameters.Add(cotp_parameter);
                analysisData.cotp_paramsCount++;
                if (cotp_parameter.paramCode == 0xc1) analysisData.cotp_rack = BitConverter.ToUInt16(cotp_parameter.param.Reverse().ToArray());
                if (cotp_parameter.paramCode == 0xc2) analysisData.cotp_solt = BitConverter.ToUInt16(cotp_parameter.param.Reverse().ToArray());
                index = index + 2 + cotp_parameter.paramLength;
            }
        }
        /// <summary>
        /// 数据传输头部解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void DT_HeaderAnalysis(byte[] data)
        {
            if (data.Length < 10) { throw new Exception("会话层/表示层/pdu类型：数据传输/头部长度太短"); }
            analysisData.dt_header_protocolID = data[0];
            try { analysisData.dt_header_roscrt = (DT_ROSCTR)data[1]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT类型错误"); }
            analysisData.dt_header_reserve = BitConverter.ToUInt16(data, 2);
            analysisData.dt_header_sequenceID = BitConverter.ToUInt16(data, 4);
            analysisData.dt_header_paramLength = BitConverter.ToUInt16(data.Skip(6).Take(2).Reverse().ToArray());
            analysisData.dt_header_dataLength = BitConverter.ToUInt16(data.Skip(8).Take(2).Reverse().ToArray());
            switch (analysisData.dt_header_roscrt)
            {
                case DT_ROSCTR.JOB:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：作业请求/暂不支持该请求");
                case DT_ROSCTR.ACK:
                    return;
                case DT_ROSCTR.ACK_DATA:
                    if (data.Length < 12) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/头部长度太短"); }
                    analysisData.dt_header_errorClass = data[10];
                    analysisData.dt_header_errorCode = data[11];
                    if (analysisData.dt_header_paramLength < 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/参数长度太短"); }
                    if (data.Length - 12 != analysisData.dt_header_paramLength + analysisData.dt_header_dataLength) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/参数长度加数据长度错误"); }
                    DT_ParamenterAnalysis(data.Skip(12).ToArray());
                    break;
                case DT_ROSCTR.USERDATA://原始协议扩展，参数字段包含请求/响应ID
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：原始协议扩展/暂不支持该请求");
                default:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：未知");
            }
        }
        /// <summary>
        /// 应用层数据传输解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void DT_ParamenterAnalysis(byte[] data)
        {
            try { analysisData.dt_parameter_function = (DT_PARAMETER_FUCTION)data[0]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码错误"); }
            analysisData.dt_parameter_itemCount = data[1];
            switch (analysisData.dt_parameter_function)
            {
                case DT_PARAMETER_FUCTION.Setup_Communication:
                    if (data.Length != 8) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：建立通信/数据长度错误"); }
                    analysisData.dt_stepCommunication_maxAmQCalling = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray());
                    analysisData.dt_stepCommunication_maxAmQCalled = BitConverter.ToUInt16(data.Skip(4).Take(2).Reverse().ToArray());
                    analysisData.dt_stepCommunication_pduLength = BitConverter.ToUInt16(data.Skip(6).Take(2).Reverse().ToArray());
                    return;
                case DT_PARAMETER_FUCTION.Read_Variable://读取值
                    if (data.Length < 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/参数长度错误"); }
                    analysisData.dt_items.Clear();
                    if (analysisData.dt_parameter_itemCount == 0 && data.Length > 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/数据长度错误"); }
                    if (analysisData.dt_parameter_itemCount == 0) { return; }
                    DT_ReadDataAnalysis(data.Skip(2).ToArray());
                    return;
                case DT_PARAMETER_FUCTION.Write_Variable://写入值
                    analysisData.dt_items.Clear();
                    if (data.Length - 2 != analysisData.dt_parameter_itemCount) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：写入值/数据长度错误"); };
                    if (analysisData.dt_parameter_itemCount == 0) { return; };
                    //写入结果
                    AckItem item = new AckItem();
                    for (int i = 1; i <= analysisData.dt_parameter_itemCount;)
                    {
                        item.returnCode = (DT_RETRUN_CODE)data[i + 1];
                        analysisData.dt_items.Add(item);
                    }
                    return;
                default:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：未知");
            }
        }
        void DT_ReadDataAnalysis(byte[] data)
        {
            int index = 0;
            AckItem item = new AckItem();
            for (int i = 1; i <= analysisData.dt_parameter_itemCount;)
            {
                if (data.Length < index + 4) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item长度错误"); }
                //获取数据和格式和结果                     
                try { item.returnCode = (DT_RETRUN_CODE)data[index]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的returnCode类型错误"); }
                try { item.transportSizes = (DT_DATA_TRANSPORT_SIZI)data[index + 1]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的transportSizes类型错误"); }
                item.dataLength = BitConverter.ToUInt16(data.Skip(6).Take(index + 2).Reverse().ToArray());//数据长度

                if (data.Length < index + 2 + item.dataLength) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的数据长度错误"); }
                item.data=new byte[item.dataLength];
                Array.Copy(data, index + 2, item.data, 0, item.dataLength);
                analysisData.dt_items.Add(item);
                index = index + 4 + item.dataLength;
            }
            if (data.Length == index) { return; };
            throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/数据总长度错误");
        }
    }
    public class SendBufferCompile
    {
        private static object connectLocker = new object();
        private static object communicationtLocker = new object();
        private static object readLocker =new object();
        private static object writeLocker = new object();
        private static object itemLocker = new object();
        private static object dataLocker = new object();
        /// <summary>
        /// 建立会话buffer
        /// </summary>
        /// <param name="rack"></param>
        /// <param name="solt"></param>
        /// <returns></returns>
        public static byte[] ReqConnectBuffer(ushort destinationRef, ushort sourcenRef, ushort rack, ushort solt)
        {
            lock (connectLocker)
            {
                byte[] bytes = new byte[22];
                //会话层4byte
                bytes[0] = 0x03;//版本号
                bytes[1] = 0x00;//保留
                Array.Copy(BitConverter.GetBytes(0x0016).Reverse().ToArray(), 0, bytes, 2, 2);//总长度
                //表示层18byte
                bytes[4] = 0x11;//表示层长度（不算自己）
                bytes[5] = 0x0e;//pud类型-连接请求
                Array.Copy(BitConverter.GetBytes(destinationRef).Reverse().ToArray(), 0, bytes, 6, 2);//目标引用
                Array.Copy(BitConverter.GetBytes(sourcenRef).Reverse().ToArray(), 0, bytes, 8, 2);//源引用
                bytes[10] = 0x00;//扩展格式/流控制
                                 //参数
                bytes[11] = 0xc0;//param1_code:TPDU-Sise
                bytes[12] = 0x01;//param1_length
                bytes[13] = 0x0a;//param1_data
                bytes[14] = 0xc1;//param2_code:SRC-TASP/Rack
                bytes[15] = 0x02;//param2_length
                Array.Copy(BitConverter.GetBytes(rack).Reverse().ToArray(), 0, bytes, 16, 2);//param2_data
                bytes[18] = 0xc2;//param3_code:DST-TASP/Solt
                bytes[19] = 0x02;//param3_length
                Array.Copy(BitConverter.GetBytes(solt).Reverse().ToArray(), 0, bytes, 20, 2);//param3_data
                return bytes;
            }
        }
        /// <summary>
        /// 数据传输，建立通信
        /// </summary>
        /// <returns></returns>
        public static byte[] ReqStepCommunicationBuffer(ushort destinationRef, ushort SequenceID)
        {
            lock (communicationtLocker)
            {
                byte[] bytes = new byte[22];
                //会话层4byte
                bytes[0] = 0x03;//版本号
                bytes[1] = 0x00;//保留
                Array.Copy(BitConverter.GetBytes(0x001a).Reverse().ToArray(), 0, bytes, 2, 2);//总长度
                                                                                              //表示层18byte
                bytes[4] = 0x02;//长度
                bytes[5] = (byte)COTP_PDU_TYPE.DT;
                Array.Copy(BitConverter.GetBytes(destinationRef).Reverse().ToArray(), 0, bytes, 6, 2);//目标引用
                                                                                                      //Header
                bytes[8] = 0x32;//协议id
                bytes[9] = (byte)DT_ROSCTR.JOB;
                bytes[10] = 0x00;//预留
                bytes[11] = 0x00;//预留
                Array.Copy(BitConverter.GetBytes(SequenceID), 0, bytes, 12, 2);//协议序列，每次请求+1
                Array.Copy(BitConverter.GetBytes(0x0008).Reverse().ToArray(), 0, bytes, 14, 2);//参数长度
                Array.Copy(BitConverter.GetBytes(0x0000).Reverse().ToArray(), 0, bytes, 16, 2);//数据长度
                                                                                               //parameter
                bytes[18] = 0xf0;//功能码-建立通信
                bytes[19] = 0x00;//预留
                Array.Copy(BitConverter.GetBytes(0x0001).Reverse().ToArray(), 0, bytes, 20, 2);//最大AmQ(Calling)
                Array.Copy(BitConverter.GetBytes(0x0001).Reverse().ToArray(), 0, bytes, 22, 2);//最大AmQ(Called)
                Array.Copy(BitConverter.GetBytes(0x03c0).Reverse().ToArray(), 0, bytes, 24, 2);//协商pdu长度-0x03c0-960
                return bytes;
            }
        }
        /// <summary>
        /// 数据传输，读取值编译
        /// </summary>
        /// <returns></returns>
        public static byte[] ReqReadBuffer(ushort destinationRef, ushort SequenceID, Variable[] readItems)
        {
            lock (readLocker)
            {
                if (readItems == null) { throw new Exception(nameof(readItems) + "为空"); }
                List<byte> bytes = new List<byte>();
                //item
                foreach (Variable reqItem in readItems)
                {
                    bytes.AddRange(ReqItemBuffer(reqItem));
                }
                byte[] bytesTemp = new byte[20];
                //会话层4byte
                bytesTemp[0] = 0x03;//版本号
                bytesTemp[1] = 0x00;//保留
                Array.Copy(BitConverter.GetBytes((ushort)bytesTemp.Length + (ushort)bytes.Count).Reverse().ToArray(), 0, bytesTemp, 2, 2);//总长度
                                                                                                                                          //表示层18byte
                bytesTemp[4] = 0x02;//长度
                bytesTemp[5] = (byte)COTP_PDU_TYPE.DT;
                Array.Copy(BitConverter.GetBytes(destinationRef).Reverse().ToArray(), 0, bytesTemp, 6, 2);//目标引用
                                                                                                          //Header
                bytesTemp[8] = 0x32;//协议id
                bytesTemp[9] = (byte)DT_ROSCTR.JOB;
                bytesTemp[10] = 0x00;//预留
                bytesTemp[11] = 0x00;//预留
                Array.Copy(BitConverter.GetBytes(SequenceID), 0, bytesTemp, 12, 2);//协议序列，每次请求+1
                Array.Copy(BitConverter.GetBytes((ushort)bytes.Count).Reverse().ToArray(), 0, bytesTemp, 14, 2);//参数长度
                Array.Copy(BitConverter.GetBytes(0x0000).Reverse().ToArray(), 0, bytesTemp, 16, 2);//数据长度
                                                                                                   //parameter
                bytesTemp[18] = (byte)DT_PARAMETER_FUCTION.Read_Variable;
                bytesTemp[19] = (byte)readItems.Length;
                bytes.InsertRange(0, bytesTemp);
                return bytes.ToArray();
            }
        }
        /// <summary>
        /// 数据传输，写入值编译
        /// </summary>
        /// <returns></returns>
        public static byte[] ReqWriteBuffer(ushort destinationRef, ushort SequenceID, Variable[] reqItems)
        {
            lock (writeLocker)
            {
                if (reqItems == null) { throw new Exception(nameof(reqItems) + "为空"); }
                byte[] bytesTemp = new byte[20];
                List<byte> bytes = new List<byte>();
                ushort paramsLength;
                ushort datasLength;
                //item
                foreach (Variable reqItem in reqItems)
                {
                    bytes.AddRange(ReqItemBuffer(reqItem));
                }
                paramsLength = (ushort)(bytes.Count + 2);
                //data
                foreach (Variable reqItem in reqItems)
                {
                    bytes.AddRange(ReqDataBuffer(reqItem));
                }
                datasLength = (ushort)(bytes.Count - paramsLength);
                //会话层4byte
                bytesTemp[0] = 0x03;//版本号
                bytesTemp[1] = 0x00;//保留
                Array.Copy(BitConverter.GetBytes((ushort)(paramsLength + datasLength + 18)).Reverse().ToArray(), 0, bytesTemp, 2, 2);//总长度
                                                                                                                                     //表示层18byte
                bytesTemp[4] = 0x02;//长度
                bytesTemp[5] = (byte)COTP_PDU_TYPE.DT;
                Array.Copy(BitConverter.GetBytes(destinationRef), 0, bytesTemp, 6, 2);//目标引用
                                                                                      //Header
                bytesTemp[8] = 0x32;//协议id
                bytesTemp[9] = (byte)DT_ROSCTR.JOB;
                bytesTemp[10] = 0x00;//预留
                bytesTemp[11] = 0x00;//预留
                Array.Copy(BitConverter.GetBytes(SequenceID), 0, bytesTemp, 12, 2);//协议序列，每次请求+1
                Array.Copy(BitConverter.GetBytes(paramsLength).Reverse().ToArray(), 0, bytesTemp, 14, 2);//参数长度
                Array.Copy(BitConverter.GetBytes(datasLength).Reverse().ToArray(), 0, bytesTemp, 16, 2);//数据长度
                                                                                                        //parameter
                bytesTemp[18] = (byte)DT_PARAMETER_FUCTION.Read_Variable;//
                bytesTemp[19] = (byte)reqItems.Length;
                bytes.InsertRange(0, bytesTemp);
                return bytes.ToArray();
            }
        }
        private static byte[] ReqDataBuffer(Variable reqItem)
        {
            lock (dataLocker)
            {
                List<byte> bytes = new List<byte>();
                bytes.Add((byte)DT_RETRUN_CODE.Reserved);
                bytes.Add((byte)reqItem.transportSizes);
                bytes.AddRange(BitConverter.GetBytes(reqItem.dataLength).Reverse().ToList());
                bytes.AddRange(reqItem.data);
                return bytes.ToArray();
            }
        }
        private static byte[] ReqItemBuffer(Variable reqItem)
        {
            lock (itemLocker)
            {
                byte[] bytes = new byte[11];
                bytes[0] = 0x12;
                bytes[1] = 0x0a;
                bytes[2] = (byte)reqItem.syntaxId;
                bytes[3] = (byte)reqItem.transportSizes;
                Array.Copy(BitConverter.GetBytes(reqItem.dataLength).Reverse().ToArray(), 0, bytes, 4, 2);
                Array.Copy(BitConverter.GetBytes(reqItem.dbNumber).Reverse().ToArray(), 0, bytes, 6, 2);
                bytes[8] = (byte)reqItem.area;
                Array.Copy(BitConverter.GetBytes(reqItem.adrress).Skip(1).Reverse().ToArray(), 0, bytes, 9, 3);
                return bytes;
            }
        }
    }
    /// <summary>
    /// 变量管理器
    /// </summary>
    public class VariablesManager
    {
        /// <summary>
        /// 变量管理器
        /// </summary>
        /// <param name="plc_type">涉及到最大收发数据编组</param>
        /// <param name="isReadOrWrite">涉及到最大收发数据编组</param>
        public VariablesManager(PLC_TYPE plc_type,bool isReadOrWrite)
        {
            if (plc_type == PLC_TYPE.S7_1200) { maxPduLength = 480; }
            else if (plc_type == PLC_TYPE.S7_1500) { maxPduLength = 960; }
            pduLength = 18;
            this.isReadOrWrite = isReadOrWrite;
        }
        private Dictionary<string, Variable> variables = new Dictionary<string, Variable>();
        private List<List<Variable>> variablesGroups = new List<List<Variable>>();
        private int pduLength;
        private int maxPduLength;
        private bool isReadOrWrite;
        object locker = new object();
        public object? Read(string name, bool result)
        {
            try
            {
                Variable variable = variables[name];
                if (variable.dateType == VARIABLE_TYPE.Bool)
                {
                    if (variable.Count == 1)
                    {
                        return variable.data[0] == 1;
                    }
                    else
                    {
                        bool[] datas = new bool[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = variable.data[i] == 1;
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Byte)
                {
                    if (variable.Count == 1)
                    {
                        return variable.data[0];
                    }
                    else
                    {
                        byte[] datas = new byte[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = variable.data[i];
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Word || variable.dateType == VARIABLE_TYPE.Int)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToInt16(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        short[] datas = new short[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToInt16(variable.data.Skip(i * 2).Take(2).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.UInt)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToInt16(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        ushort[] datas = new ushort[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToUInt16(variable.data.Skip(i * 2).Take(2).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.DWord || variable.dateType == VARIABLE_TYPE.DInt)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToInt32(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        int[] datas = new int[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToInt32(variable.data.Skip(i * 4).Take(4).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.UDInt)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToUInt32(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        uint[] datas = new uint[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToUInt32(variable.data.Skip(i * 4).Take(4).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Real)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToSingle(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        float[] datas = new float[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToSingle(variable.data.Skip(i * 4).Take(4).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.ULInt)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToUInt64(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        UInt64[] datas = new UInt64[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToUInt64(variable.data.Skip(i * 8).Take(8).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.LInt)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToInt64(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        Int64[] datas = new Int64[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToInt64(variable.data.Skip(i * 8).Take(8).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.LReal)
                {
                    if (variable.Count == 1)
                    {
                        return BitConverter.ToDouble(variable.data.Reverse().ToArray());
                    }
                    else
                    {
                        double[] datas = new double[variable.Count];
                        for (int i = 0; i < datas.Length - 1; i++)
                        {
                            datas[i] = BitConverter.ToDouble(variable.data.Skip(i * 8).Take(8).Reverse().ToArray());
                        }
                        return datas;
                    }
                }
                else throw new Exception("读取错误/数据类型错误");

            }
            catch (Exception ex)
            {
                throw new Exception("读取错误", ex);
            }
        }
        public void Write(string name, object value)
        {
            Write(name, new object[1] { value });
        }
        /// <summary>
        /// 写入值
        /// </summary>
        /// <param name="name">变量名称</param>
        /// <param name="value">变量数组值</param>
        /// <returns>ture：写入成功，不代表发送成功，false：写入失败</returns>
        public void Write(string name, object[] value)
        {
            try
            {
                Variable variable = variables[name];
                if (variable.dateType == VARIABLE_TYPE.Bool)
                {
                    if (value.Length != variable.Count) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        variable.data[i] = (bool)(value[i]) ? (byte)1 : (byte)0;
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Byte)
                {
                    if (value.Length != variable.Count) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        variable.data[i] = (byte)value[i];
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Word || variable.dateType == VARIABLE_TYPE.Int)
                {
                    if (value.Length != variable.Count * 2) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((Int16)value[i]).Reverse().ToArray(), 0, variable.data, i * 2, 2);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.UInt)
                {
                    if (value.Length != variable.Count * 2) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((UInt16)value[i]).Reverse().ToArray(), 0, variable.data, i * 2, 2);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.DWord || variable.dateType == VARIABLE_TYPE.DInt)
                {
                    if (value.Length != variable.Count * 4) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((Int32)value[i]).Reverse().ToArray(), 0, variable.data, i * 4, 4);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.UDInt)
                {
                    if (value.Length != variable.Count * 4) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((UInt32)value[i]).Reverse().ToArray(), 0, variable.data, i * 4, 4);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.Real)
                {
                    if (value.Length != variable.Count * 4) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((float)value[i]).Reverse().ToArray(), 0, variable.data, i * 4, 4);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.LInt)
                {
                    if (value.Length != variable.Count * 8) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((Int64)value[i]).Reverse().ToArray(), 0, variable.data, i * 8, 8);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.ULInt)
                {
                    if (value.Length != variable.Count * 8) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((UInt64)value[i]).Reverse().ToArray(), 0, variable.data, i * 8, 8);
                    }
                }
                else if (variable.dateType == VARIABLE_TYPE.LReal)
                {
                    if (value.Length != variable.Count * 8) { throw new Exception("写入数组长度错误"); }
                    for (int i = 0; i < variable.Count - 1; i++)
                    {
                        Array.Copy(BitConverter.GetBytes((double)value[i]).Reverse().ToArray(), 0, variable.data, i * 8, 8);
                    }
                }
                variables[name] = variable;
            }
            catch (Exception e)
            {
                throw new Exception("写入错误", e);
            }
        }
        /// <summary>
        /// 加载请求变量组
        /// </summary>
        /// <param name="variablesGroups"></param>
        public void Load(out List<List<Variable>> variablesGroups)
        {
            lock (locker)
            {
                variablesGroups = this.variablesGroups;
            }
        }
        /// <summary>
        /// 更新返回变量结果
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="returnCode"></param>
        public void Updata(string name, byte[] data,DT_RETRUN_CODE returnCode)
        {
            Variable variable = variables[name];
            variable.data = data;
            variable.returnCode = returnCode;
            variables[name] = variable;
        }
        /// <summary>
        /// 更新返回变量结果
        /// </summary>
        /// <param name="name"></param>
        /// <param name="returnCode"></param>
        public void Updata(string name, DT_RETRUN_CODE returnCode)
        {
            Variable variable = variables[name];
            variable.returnCode = returnCode;
            variables[name] = variable;
        }
        /// <summary>
        /// 添加变量
        /// </summary>
        /// <param name="name">变量名称</param>
        /// <param name="adrress">地址，字符大写，例如：DB10.DBX10.0，DB10.DBB10，DB10.DBW10,DB10.DBD10，M10.0，I10.0，Q10.0</param>
        /// <param name="dateType">数据类型</param>
        /// <param name="count">数量=1为单个变量，大于1为数组</param>
        /// <exception cref="Exception"></exception>
        public void AddVariable(string name, string adrress, VARIABLE_TYPE dateType, int count)
        {
            lock (locker)
            {
                Variable variable;
                try { CompileVariable(name, adrress, dateType, count, out variable); } catch (Exception e) { throw new Exception("编译变量/" + e.Message); }
                if (variables.ContainsKey(variable.name)) { throw new Exception("添加托管变量/变量已存在"); }
                try { VariablesGroup(variable); } catch (Exception e) { throw new Exception("变量排序错误/" + e.Message); }
                try { variables.Add(variable.name, variable); } catch (Exception e) { throw new Exception("添加托管变量/" + e.Message); }
            }
        }
        /// <summary>
        /// 数据编组
        /// </summary>
        /// <param name="varialbe">增加变量</param>
        private void VariablesGroup(Variable varialbe)
        {

            ushort itemLength = 0;
            List<Variable> variableList = variablesGroups.Last();
            //计算item长度
            if (isReadOrWrite)
            {
                itemLength = (ushort)(varialbe.dataLength + 4);
                if (itemLength < 12) itemLength = 12;
            }
            else
            {
                itemLength = (ushort)(varialbe.dataLength + 12);
            }     
            if (pduLength + varialbe.dataLength < maxPduLength)
            {
                //更新pdu长度
                pduLength += itemLength;
                //编组变量
                variableList.Add(varialbe);
                //更新item组分组
                variablesGroups[variablesGroups.Count - 1] = variableList;
            }
            else
            {
                //初始化PDU长度
                pduLength = 18;
                //计算item长度
                if (isReadOrWrite)
                {
                    itemLength = (ushort)(varialbe.dataLength + 4);
                    if (itemLength < 12) itemLength = 12;
                }
                else
                {
                    itemLength = (ushort)(varialbe.dataLength + 12);
                }
                //更新pdu长度
                pduLength += itemLength;
                //清除item组
                variableList.Clear();
                //增加item
                variableList.Add(varialbe);
                //增加item组分组
                variablesGroups.Add(variableList);
            }
        }
        /// <summary>
        /// 编译变量
        /// </summary> 
        /// <param name="name">变量名称</param>
        /// <param name="adrress">地址，字符大写，例如：DB10.DBX10.0，DB10.DBB10，DB10.DBW10,DB10.DBD10，M10.0，I10.0，Q10.0</param>
        /// <param name="dateType">数据类型</param>
        /// <param name="count">数量=1为单个变量，大于1为数组</param>
        /// <param name="variable">返回编译变量</param>
        /// <exception cref="Exception"></exception>
        private void CompileVariable(string name, string adrress, VARIABLE_TYPE dateType, int count, out Variable variable)
        {
            variable = new Variable();
            //确定数据长度
            if (dateType == VARIABLE_TYPE.Bool)
            {
                if (count <= 1) variable.dataLength = 1;
                else variable.dataLength = (ushort)count;
                variable.data = new byte[variable.dataLength];
            }
            else if (dateType == VARIABLE_TYPE.Byte)
            {
                if (count <= 1) variable.dataLength = 1;
                else variable.dataLength = (ushort)count;
                variable.data = new byte[variable.dataLength];
            }
            else if (dateType == VARIABLE_TYPE.Word || dateType == VARIABLE_TYPE.Int || dateType == VARIABLE_TYPE.UInt)
            {
                if (count <= 1) variable.dataLength = 2;
                else variable.dataLength = (ushort)(count * 2);
                variable.data = new byte[variable.dataLength];
            }
            else if (dateType == VARIABLE_TYPE.DWord || dateType == VARIABLE_TYPE.DInt || dateType == VARIABLE_TYPE.UDInt || dateType == VARIABLE_TYPE.Real)
            {
                if (count <= 1) variable.dataLength = 4;
                else variable.dataLength = (ushort)(count * 4);
                variable.data = new byte[variable.dataLength];
            }
            else if (dateType == VARIABLE_TYPE.LInt || dateType == VARIABLE_TYPE.ULInt || dateType == VARIABLE_TYPE.LReal)
            {
                if (count <= 1) variable.dataLength = 8;
                else variable.dataLength = (ushort)(count * 8);
                variable.data = new byte[variable.dataLength];
            }
            if (name == null) { throw new Exception(nameof(name) + "：为空"); }
            if (adrress == null) { throw new Exception(nameof(adrress) + "：为空"); }
            variable.name = name;
            variable.specificationType = 0x12;
            variable.itemLength = 0x0a;
            variable.syntaxId = DT_DATA_SYNTAX_ID.S7ANY;
            int byteOffset;
            int bitOffset;
            try
            {
                string[] strings = adrress.Split(".");
                //解析地址      
                if (strings[0].Substring(0, 1) == "M" || strings[0].Substring(0, 1) == "I" || strings[0].Substring(0, 1) == "Q")
                {
                    variable.dbNumber = 0;
                    if (strings[0].Substring(0, 1) == "M")
                    {
                        variable.area = DT_DATA_AREA.Flags;
                    }
                    else if (strings[0].Substring(0, 1) == "I")
                    {
                        variable.area = DT_DATA_AREA.Inputs;
                    }
                    else if (strings[0].Substring(0, 1) == "Q")
                    {
                        variable.area = DT_DATA_AREA.Outputs;
                    }
                    try
                    {
                        //地址偏移
                        try { byteOffset = ushort.Parse(strings[0].Remove(0, 1)); } catch { throw new Exception("字节偏移错误"); }
                        try { bitOffset = ushort.Parse(strings[1]); }
                        catch { if (dateType == VARIABLE_TYPE.Bool) { throw new Exception("位偏移错误"); } else { bitOffset = 0; } }
                        if (bitOffset < 0 || bitOffset > 7) { throw new Exception("位偏移错误"); }
                        if (bitOffset != 0 && dateType != VARIABLE_TYPE.Bool) { throw new Exception("位偏移与数据类型不匹配"); }
                        //数据类型和长度
                        if (dateType == VARIABLE_TYPE.Bool)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.Bit;
                        }
                        else if (dateType == VARIABLE_TYPE.Byte)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else if (dateType == VARIABLE_TYPE.Word || dateType == VARIABLE_TYPE.Int || dateType == VARIABLE_TYPE.UInt)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else if (dateType == VARIABLE_TYPE.DWord || dateType == VARIABLE_TYPE.DInt || dateType == VARIABLE_TYPE.UDInt || dateType == VARIABLE_TYPE.Real)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else if (dateType == VARIABLE_TYPE.LInt || dateType == VARIABLE_TYPE.ULInt || dateType == VARIABLE_TYPE.LReal)
                        {
                            throw new Exception("该地址类型不支持64位数据类型");
                        }
                        else { throw new Exception("数据类型未知"); }
                    }
                    catch { throw new Exception("偏移类型错误"); }
                }
                else if (strings[0].Substring(0, 2) == "DB")
                {
                    variable.area = DT_DATA_AREA.DataBlocks;
                    try { variable.dbNumber = ushort.Parse(strings[0].Remove(0, 2)); } catch { throw new Exception("DB号错误"); }
                    try
                    {
                        //偏移解析
                        if (strings[1].Substring(0, 3) == "DBX")
                        {
                            try { byteOffset = ushort.Parse(strings[1].Remove(0, 3)); } catch { throw new Exception("字节偏移错误"); }
                            try { bitOffset = ushort.Parse(strings[2]); } catch { throw new Exception("位偏移错误"); }
                            if (bitOffset < 0 || bitOffset > 7) { throw new Exception("位偏移错误"); }
                            if (bitOffset != 0 && dateType != VARIABLE_TYPE.Bool) { throw new Exception("地址类型与数据类型不匹配"); }
                        }
                        else if (strings[1].Substring(0, 3) == "DBB" || strings[1].Substring(0, 3) == "DBW" || strings[1].Substring(0, 3) == "DBD")
                        {
                            try { byteOffset = ushort.Parse(strings[1].Remove(0, 3)); } catch { throw new Exception("DBX字节偏移错误"); }
                            if (dateType == VARIABLE_TYPE.Bool) { throw new Exception("地址类型与数据类型不匹配"); }
                            bitOffset = 0;
                        }
                        else { throw new Exception("DB数据类型错误"); }
                        //类型解析
                        if (dateType == VARIABLE_TYPE.Bool)
                        {
                            try { bitOffset = ushort.Parse(strings[1]); } catch { throw new Exception("位偏移错误"); }
                            if (bitOffset < 0 || bitOffset > 7) { throw new Exception("位偏移错误"); }
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.Bit;
                        }
                        else if (dateType == VARIABLE_TYPE.Byte)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;

                        }
                        else if (dateType == VARIABLE_TYPE.Word || dateType == VARIABLE_TYPE.Int || dateType == VARIABLE_TYPE.UInt)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else if (dateType == VARIABLE_TYPE.DWord || dateType == VARIABLE_TYPE.DInt || dateType == VARIABLE_TYPE.UDInt || dateType == VARIABLE_TYPE.Real)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else if (dateType == VARIABLE_TYPE.LInt || dateType == VARIABLE_TYPE.ULInt || dateType == VARIABLE_TYPE.LReal)
                        {
                            variable.transportSizes = DT_DATA_TRANSPORT_SIZI.BYTE;
                        }
                        else { throw new Exception("数据类型未知"); }
                    }
                    catch { throw new Exception("DB数据类型错误"); }
                }
                else { throw new Exception("地址类型错误"); }
            }
            catch { throw new Exception("地址类型错误"); }
            variable.adrress = byteOffset * 8 + bitOffset;
            if (variable.dataLength > maxPduLength - 22) { throw new Exception("数据长度过长"); }
        }
    }
    /// <summary>
    /// 变量管理
    /// </summary>
    public class PLC
    {
        /// <summary>
        /// 目标引用，唯一标识
        /// </summary>
        public ushort destinationRef;
        /// <summary>
        /// 源引用，唯一标识
        /// </summary>
        public ushort sourcenRef;
        ///<summary>
        /// 机架号
        /// </summary>
        public ushort rack;
        /// <summary>
        /// 槽号
        /// </summary>
        public ushort solt;
        /// <summary>
        /// 调用的最大并行作业数
        /// </summary>
        public ushort maxAmQCalling;
        /// <summary>
        /// 已调用的最大并行作业数
        /// </summary>
        public ushort maxAmQCalled;
        /// <summary>
        /// 协商pdu长度
        /// </summary>
        public ushort pduLength;
        /// <summary>
        /// 请求序列
        /// </summary>
        public ushort sequenceID;
        object sequenceLocker=new object();
        /// <summary>
        /// 序列索引
        /// </summary>
        private Socket socket;
        private IPEndPoint remoteEndPoint;
        private object locker=new object();
        private bool isSocketInited;
        private bool isConected;
        private int timeOut;
        public PLC(IPAddress ip,int port,ushort rack,ushort solt)
        {
            this.rack = rack;
            this.solt = solt;
            remoteEndPoint = new IPEndPoint(ip, port);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            isSocketInited = false;
            isConected = false;
            timeOut = 10000;
        }

        public void Read(VariablesManager variablesManager)
        {
            variablesManager.Load(out List<List<Variable>> variablesGroups);
            lock (locker)
            {
                //建立通信连接
                Task.Run(() =>
                {
                    Connect();
                });
                int lastTime=Environment.TickCount;
                while(!isConected)
                {
                    if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                    {
                        throw new Exception("连接超时");
                    }
                    Task.Delay(0);
                }
                ArraySegment<byte> buffer;
                foreach (var items in variablesGroups)
                {
                    //发送读取请求
                    ushort sequenceId = SequenceID();
                    bool isSend = false;
                    Task.Run(() =>
                    {
                        socket.Send(SendBufferCompile.ReqReadBuffer(destinationRef, sequenceId, items.ToArray()));
                        isSend = true;
                    });
                    lastTime = Environment.TickCount;
                    while (!isSend)
                    {
                        if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                        {
                            throw new Exception("发送超时");
                        }
                        Task.Delay(0);
                    }
                    //接收读取响应
                    buffer = new ArraySegment<byte>();
                    int length=0;
                    bool isRead = false;
                    lastTime = Environment.TickCount;
                    Task.Run(() =>
                    {
                        length = socket.Receive(buffer);
                        isRead=true;
                    });
                    while (!isRead)
                    {
                        if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                        {
                            throw new Exception("接收超时");
                        }
                        Task.Delay(0);
                    }
                    //分析接数据
                    RecieveBufferAnalysis bufferAnalysis = new RecieveBufferAnalysis();
                    bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                    if (bufferAnalysis.AnalysisData.dt_header_sequenceID == sequenceId)
                    {
                        for (int i = 0; i < items.Count; i++)
                        {
                            
                            if ( items[i].itemLength== bufferAnalysis.AnalysisData.dt_items[i].dataLength && bufferAnalysis.AnalysisData.dt_items[i].returnCode==DT_RETRUN_CODE.Success)
                            {
                                variablesManager.Updata(items[i].name, bufferAnalysis.AnalysisData.dt_items[i].data, bufferAnalysis.AnalysisData.dt_items[i].returnCode);
                            }
                        }
                    }
                }
                socket.Disconnect(false);
            }
        }

        private bool Connect()
        {
            try
            {
                //连接
                socket.Connect(remoteEndPoint);
                //清除缓存区数据
                if (socket.Available > 0)
                {
                    byte[] bufferTemp = new byte[socket.Available];
                    socket.Receive(bufferTemp);
                }
                //发送s7连接
                socket.Send(SendBufferCompile.ReqConnectBuffer(209, 209, 0, 1));
                //接收s7连接确认
                ArraySegment<byte> buffer = new ArraySegment<byte>();
                int length = socket.Receive(buffer);
                //分析接数据
                RecieveBufferAnalysis bufferAnalysis = new RecieveBufferAnalysis();
                bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                if (bufferAnalysis.AnalysisData.cotp_pduType != COTP_PDU_TYPE.DT) return false;
                rack = bufferAnalysis.AnalysisData.cotp_rack;
                solt = bufferAnalysis.AnalysisData.cotp_solt;
                //发送s7建立通信
                ushort sequenceId = SequenceID();
                socket.Send(SendBufferCompile.ReqStepCommunicationBuffer(209, sequenceId));
                //接收s7建立通信
                buffer = new ArraySegment<byte>();
                length = socket.Receive(buffer);
                //分析接数据
                bufferAnalysis = new RecieveBufferAnalysis();
                bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                //建立通信成功
                if (bufferAnalysis.AnalysisData.cotp_pduType != COTP_PDU_TYPE.DT) return false;
                if (bufferAnalysis.AnalysisData.dt_header_sequenceID != sequenceId) return false;
                if (bufferAnalysis.AnalysisData.dt_parameter_function != DT_PARAMETER_FUCTION.Setup_Communication) return false;
                maxAmQCalling = bufferAnalysis.AnalysisData.dt_stepCommunication_maxAmQCalling;
                maxAmQCalled = bufferAnalysis.AnalysisData.dt_stepCommunication_maxAmQCalled;
                pduLength = bufferAnalysis.AnalysisData.dt_stepCommunication_pduLength;
                return true;
            }
            catch { return false; }
        }

        private ushort SequenceID()
        {
            lock (sequenceLocker)
            {
                return (ushort)sequenceID++;
            }
        }
    }

}
